package pagination

import (
	"zong-go/pkg/config"
	"zong-go/pkg/helper"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"math"
	"strconv"
	"strings"
)

// baseObject 分页器对象
type baseObject struct {
	Context *gin.Context
	db      *gorm.DB
	PathUrl string
	BaseUrl string // 基础URL
	PerPage int    // 每页条数
	Page    int    // 页数
	Count   int64  // 总条数
}

// PaginationObject 分页对象
type PaginationObject struct {
	CurrentPage  int         `json:"current_page"`   // 当前页
	FirstPageURL string      `json:"first_page_url"` // 第一页URL
	From         int         `json:"from"`           // 开始页码
	LastPage     int         `json:"last_page"`      // 最后一页
	LastPageUrl  string      `json:"last_page_url"`  // 最后一页URL
	NextPageUrl  string      `json:"next_page_url"`  // 下一页URL
	Path         string      `json:"path"`           // URL地址
	PerPage      int         `json:"per_page"`       // 每页条数
	PrevPageUrl  string      `json:"prev_page_url"`  // 上一页URL
	To           int         `json:"to"`             // 结束页码
	Total        int64       `json:"total"`          // 总条数
	Data         interface{} `json:"data"`
}

// SimplePaginationObject 分页对象
type SimplePaginationObject struct {
	CurrentPage  int         `json:"current_page"`   // 当前页
	FirstPageURL string      `json:"first_page_url"` // 第一页URL
	From         int         `json:"from"`           // 开始页码
	NextPageUrl  string      `json:"next_page_url"`  // 下一页URL
	Path         string      `json:"path"`           // URL地址
	PerPage      int         `json:"per_page"`       // 每页条数
	PrevPageUrl  string      `json:"prev_page_url"`  // 上一页URL
	To           int         `json:"to"`             // 结束页码
	Data         interface{} `json:"data"`
}

// initPagination 初始化分页器
func initPagination(context *gin.Context, db *gorm.DB, data interface{}, PerPage int) *baseObject {
	// 默认每页数量
	if PerPage <= 0 {
		PerPage = helper.StringToInt(config.Get("pagination", "per_page"))
	}
	// 实例对象
	p := &baseObject{
		Context: context,
		db:      db,
		PerPage: PerPage,
		Page:    1,
		Count:   -1,
	}
	// 设置 BaseURL
	baseURL := config.Get("app", "url") + context.FullPath()
	pageParam := config.Get("pagination", "page_param")
	if strings.Contains(baseURL, "?") {
		p.BaseUrl = baseURL + "&" + pageParam + "="
	} else {
		p.BaseUrl = baseURL + "?" + pageParam + "="
	}
	p.PathUrl = baseURL
	// 设置 Page
	p.GetPageFromRequest()

	return p
}

// Pagination 分页对象构建器
// context —— 用来获取分页的 URL 参数，默认是 page，可通过 config/pagination.go 修改
// db —— GORM 查询句柄，用以查询数据集和获取数据总数
// PerPage —— 每页条数，传参为小于或者等于 0 时为默认值  10，可通过 config/pagination.go 修改
func Pagination(context *gin.Context, db *gorm.DB, data interface{}, PerPage int) (PaginationObject, error) {
	// 初始化分页器
	p := initPagination(context, db, data, PerPage)
	// 设置 Count
	p.TotalCount()
	result := PaginationObject{
		CurrentPage:  p.CurrentPage(),  // 当前页
		FirstPageURL: p.FirstPageURL(), // 第一页URL
		From:         p.From(),         // 开始页码
		LastPage:     p.LastPage(),     // 最后一页
		LastPageUrl:  p.LastPageUrl(),  // 最后一页URL
		NextPageUrl:  p.NextPageUrl(),  // 下一页URL
		Path:         p.Path(),         // URL地址
		PerPage:      p.PerPage,        // 每页条数
		PrevPageUrl:  p.PrevPageUrl(),  // 上一页URL
		To:           p.To(),           // 结束页码
		Total:        p.Count,          // 总条数
	}
	// 获取数据
	err := p.Results(data)
	result.Data = data

	return result, err
}

// SimplePaginationObject 简洁分页对象构建器
// context —— 用来获取分页的 URL 参数，默认是 page，可通过 config/pagination.go 修改
// db —— GORM 查询句柄，用以查询数据集和获取数据总数
// PerPage —— 每页条数，传参为小于或者等于 0 时为默认值  10，可通过 config/pagination.go 修改
func SimplePagination(context *gin.Context, db *gorm.DB, data interface{}, PerPage int) (SimplePaginationObject, error) {
	// 初始化分页器
	p := initPagination(context, db, data, PerPage)
	// 设置 Count
	p.TotalCount()
	result := SimplePaginationObject{
		CurrentPage:  p.CurrentPage(),  // 当前页
		FirstPageURL: p.FirstPageURL(), // 第一页URL
		From:         p.From(),         // 开始页码
		NextPageUrl:  p.NextPageUrl(),  // 下一页URL
		Path:         p.Path(),         // URL地址
		PerPage:      p.PerPage,        // 每页条数
		PrevPageUrl:  p.PrevPageUrl(),  // 上一页URL
		To:           p.To(),           // 结束页码
	}
	// 获取数据
	err := p.Results(data)
	result.Data = data

	return result, err
}

// GetPageFromRequest 从 URL 中获取 page 参数
func (p *baseObject) GetPageFromRequest() {
	page := p.Context.Query(config.Get("pagination", "page_param"))
	if page == "" {
		p.Page = 1
	} else {
		pageInt := helper.StringToInt(page)
		if pageInt <= 0 {
			p.Page = 1
		} else {
			p.Page = pageInt
		}
	}
}

// TotalCount 返回的是数据库里的条数
func (p *baseObject) TotalCount() {
	if p.Count == -1 {
		var count int64
		if err := p.db.Count(&count).Error; err != nil {
			p.Count = 0
		} else {
			p.Count = count
		}
	}
}

// Results 返回请求数据，请注意 data 参数必须为 GROM 模型的 Slice 对象
func (p baseObject) Results(data interface{}) error {
	var err error
	var offset int
	page := p.Page
	if page == 0 {
		return err
	}

	if page >= 1 {
		offset = (page - 1) * p.PerPage
	}

	return p.db.Preload(clause.Associations).Limit(p.PerPage).Offset(offset).Find(data).Error
}

// CurrentPage 当前页
func (p baseObject) CurrentPage() int {
	return p.Page
}

// FirstPageURL 第一页URL
func (p baseObject) FirstPageURL() string {
	return p.BaseUrl + strconv.Itoa(p.Page)
}

// From 开始页码
func (p baseObject) From() int {
	return p.PerPage*(p.Page-1) + 1
}

// LastPage 最后一页
func (p baseObject) LastPage() int {
	if i := int(math.Ceil(float64(p.Count / int64(p.PerPage)))); i <= 0 {
		return 1
	} else {
		return i
	}
}

// LastPageUrl 最后一页URL
func (p *baseObject) LastPageUrl() string {
	return p.BaseUrl + strconv.Itoa(p.LastPage())
}

// NextPageUrl 下一页URL
func (p baseObject) NextPageUrl() string {
	return p.BaseUrl + strconv.Itoa(p.Page+1)
}

// Path URL地址
func (p baseObject) Path() string {
	return p.PathUrl
}

// PrevPageUrl 上一页URL
func (p baseObject) PrevPageUrl() string {
	pageIndex := p.Page
	if pageIndex-1 <= 0 {
		return p.BaseUrl + strconv.Itoa(p.Page)
	}
	return p.BaseUrl + strconv.Itoa(p.Page-1)
}

// To 结束页码
func (p baseObject) To() int {
	return p.PerPage * p.Page
}
